/*
 * ThreadPool.h
 *
 *  Created on: 2013. 11. 15.
 *      Author: jjaehuny
 */

#ifndef PROJECTZ_NUTIL_THREADPOOL_H_
#define PROJECTZ_NUTIL_THREADPOOL_H_

#include <stack>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <algorithm>

namespace ProjectZ { namespace NUtil {

class ThreadPool
{
private :
	struct Worker
	{
		ThreadPool& pool_;
		Worker(ThreadPool& pool)
			: pool_(pool)
		{
		}

		void operator () ()
		{
			while(true)
			{
				std::function<void()> f;
				{
					std::unique_lock<std::mutex> lock(pool_.mutex_);
					while(false == pool_.stop_ && pool_.tasks_.empty())
					{
						pool_.condition_.wait(lock);
					}

					if(true == pool_.stop_)
					{
						return;
					}

					f = pool_.tasks_.front();
					pool_.tasks_.pop_front();
				}
				f();
			}
		}
	};
public:
	/*!
	  \param thread_count pool 내에 생성될 thread의 갯수
	 */
	ThreadPool(int thread_count)
		: stop_(false)
	{
		for(int i=0; i<thread_count; i++)
		{
			workers_.push_back(std::thread(Worker(*this)));
		}
	}
	virtual ~ThreadPool()
	{
		stop_ = true;
		condition_.notify_all();

		std::for_each (workers_.begin(), workers_.end(), [](std::thread& thr) {
			thr.join();
		});
	}
	/*!
	  \param t thread pool에서 실행 하게 될 함수자(functor)<br>
                            ※ std::bind(&some_function, arg1, arg2)
	 */
	template <class T>
	void PostTask(T t)
	{
		{
			std::unique_lock<std::mutex> lock(mutex_);
			tasks_.push_back(t);
		}
		condition_.notify_one();
	}
private :
	std::vector<std::thread > workers_;
	std::deque<std::function<void ()> > tasks_;
	bool stop_;
	std::mutex mutex_;
	std::condition_variable condition_;
};

} /**/ } /**/

#endif /* PROJECTZ_NUTIL_THREADPOOL_H_ */
